/**
 * Copyright (c) 2009 Moogene
 * All rights reserved
 *
 * This software is the confidential and proprietary information of Moogene.
 * You shall not disclose such Confidential Information and shall use it only
 * in accordance with the terms of the license agreement you entered into
 * with Moogene
 */

package com.moogene.android.engine.mmi;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.util.Log;

import com.moogene.android.InfoMediator;
import com.moogene.android.engine.action.ActTouchEvent;

/**
 * @author Song, jiawei
 */
public abstract class Page {
	final private static String TAG = "Page";

	final private Layer mBgLayer;
	
	final private ArrayList<Layer> mLayersAll;

	final private LinkedList<Layer> mLayersTop;

	final private LinkedList<Layer> mLayersMiddle;

	final private LinkedList<Layer> mLayersBottom;

	final private LinkedList<Layer> mLayersHud;

	// final private ArrayList<ActionListener> mActions;
	private ArrayList<Layer> mFocuses;

	private Layer currentFocus;

	private Layer currentHold;

	private boolean isDragLayer;

	private int dragOffX;

	private int dragOffY;

	private int holdTimer;

	private int mFoucsRow;

	private int mFoucsCol;

//	private boolean mResReady;

	private final Camera camera;

	private boolean isDragCamera;

	private Bitmap imgBg;
	
	final private Rect boarderRect;

	public Page() {
		mLayersAll = new ArrayList<Layer>();
		mLayersTop = new LinkedList<Layer>();
		mLayersMiddle = new LinkedList<Layer>();
		mLayersBottom = new LinkedList<Layer>();
		mLayersHud = new LinkedList<Layer>();

		mBgLayer = new Layer(InfoMediator.getScreenWidth(),
				InfoMediator.getScreenHeight(), 0) {
			@Override
			public void draw(Canvas c) {
				onDrawBg(c);
			}

			@Override
			public void update() {
				// TODO Auto-generated method stub

			}
		};
		camera = new Camera();
//		onInit();
		boarderRect = new Rect();
		boarderRect.left = 0;
		boarderRect.top = 0;
	}

	public abstract void onInit(String... args);

	public abstract void onStart();
	
	public void clear() {
		mLayersAll.clear();
		mLayersTop.clear();
		mLayersMiddle.clear();
		mLayersBottom.clear();
		mLayersHud.clear();
		camera.setLocation(0, 0);
	}

	protected void onDrawBg(Canvas c) {
		if (imgBg != null) {
			boolean isNeedFillBg = false;
			int x = 0;
			int y = 0;
			int gapX = (InfoMediator.getScreenWidth() - imgBg.getWidth()) >> 1;
			int gapY = (InfoMediator.getScreenHeight() - imgBg.getHeight()) >> 1;
			if (gapX > 0) {
				x = gapX;
				isNeedFillBg = true;
			}
			if (gapY > 0) {
				y = gapY;
				isNeedFillBg = true;
			}
			if (isNeedFillBg) {
				c.drawColor(Color.BLACK);
			}
			c.drawBitmap(imgBg, x, y, null);
		} else {
			c.drawColor(Color.BLACK);
		}
	}
	
	public Rect getBoarder(){
		if(imgBg != null){
			boarderRect.right = imgBg.getWidth();
			boarderRect.bottom = imgBg.getHeight();
		}else{
			boarderRect.right = InfoMediator.getScreenWidth();
			boarderRect.bottom = InfoMediator.getScreenHeight();
		}
		return boarderRect;
	}

	protected boolean canDrag() {
		return false;
	}

	public boolean isDragging() {
		return isDragCamera || isDragLayer;
	}
	
	protected boolean shouldFollowCameraWhenDrag(){
		return true;
	}

	/**
	 * @return the mBgLayer
	 */
	public Layer getBgLayer() {
		return mBgLayer;
	}

	public void setBgImg(Bitmap imgBg) {
		this.imgBg = imgBg;
		camera.setBoarder(imgBg.getWidth(), imgBg.getHeight());
	}

	public void setBgImg(int resId) {
		setBgImg(InfoMediator.getBitmap(resId));
	}

	public void addLayer(Layer layer) {
		if(mLayersAll.contains(layer)){
			return;
		}
		mLayersAll.add(layer);
		if (layer.getPriority() == Layer.BOTTOM) {
			mLayersBottom.add(layer);
		} else if (layer.getPriority() == Layer.MIDDLE) {
			mLayersMiddle.add(layer);
		} else if (layer.getPriority() == Layer.TOP) {
			mLayersTop.add(layer);
		} else if (layer.getPriority() == Layer.HUD) {
			mLayersHud.add(layer);
		}
	}

	public void removeLayer(Layer layer) {
		if(mLayersAll.remove(layer) != true){
			return;
		}
		if (layer.getPriority() == Layer.BOTTOM) {
			mLayersBottom.remove(layer);
		} else if (layer.getPriority() == Layer.MIDDLE) {
			mLayersMiddle.remove(layer);
		} else if (layer.getPriority() == Layer.TOP) {
			mLayersTop.remove(layer);
		} else if (layer.getPriority() == Layer.HUD) {
			mLayersTop.remove(layer);
		}
	}

	public void setKeyboardFocus(int col, int row) {
		mFoucsCol = col;
		mFoucsRow = row;
		mFocuses = new ArrayList<Layer>();
	}

	public void addFocusable(Layer fl) {
		mFocuses.add(fl);
	}

//	public void loadRes() {
//		mResReady = true;
//	}
//
//	public void releaseRes() {
//		mResReady = false;
//	}

	private void updateLayers(LinkedList<Layer>... layerLists) {
		for (LinkedList<Layer> list : layerLists) {
			@SuppressWarnings("unchecked")
			LinkedList<Layer> colList = (LinkedList<Layer>) list.clone();
			for (Layer l : colList) {
				if(l.canAutoRemove()){
					if(!Rect.intersects(getBoarder(), l.getDstRect())){
						list.remove(l);
					}
				}
				l.update();
			}
		}
	}

	private void updateLayersState(LinkedList<Layer>... layerLists) {
		for (LinkedList<Layer> list : layerLists) {
			@SuppressWarnings("unchecked")
			LinkedList<Layer> colList = (LinkedList<Layer>) list.clone();
			for (Layer l : colList) {
				l.updateState();
			}
		}
	}
	
	private boolean doLayersAction(ActTouchEvent actionEvent, LinkedList<Layer>... layerLists) {
		for (LinkedList<Layer> list : layerLists) {
			@SuppressWarnings("unchecked")
			Iterator<Layer> it= ((LinkedList<Layer>)list.clone()).descendingIterator();
			while(it.hasNext()){
				Layer l = it.next();
				if(doLayerAction(l, actionEvent)){
					return true;
				}
			}
		}
		return false;
	}
	
	
	private void drawLayer(Canvas canvas, LinkedList<Layer>... layerLists){
		for (LinkedList<Layer> list : layerLists) {
			for (Layer l : list) {
				if(!l.isHide()){
					l.draw(canvas);
				}
			}
		}
	}

	@SuppressWarnings("unchecked")
	public void update() {
		camera.update();
		updateLayers(mLayersBottom, mLayersMiddle, mLayersTop, mLayersHud);
		updateLayersState(mLayersBottom, mLayersMiddle, mLayersTop, mLayersHud);
	}

//	boolean isResReady() {
//		return mResReady;
//	}

	@SuppressWarnings("unchecked")
	void draw(Canvas canvas) {
		int count = canvas.save();
		canvas.translate(-camera.x, -camera.y);
		mBgLayer.draw(canvas);
		drawLayer(canvas, mLayersBottom, mLayersMiddle, mLayersTop);
		canvas.restoreToCount(count);
		drawLayer(canvas, mLayersHud);
	}

	@SuppressWarnings("unchecked")
	public boolean actionPerformed(ActTouchEvent actionEvent) {
		log("actionPerformed(); Type = " + actionEvent.getType() + "; X = "
				+ actionEvent.getX() + "; Y = " + actionEvent.getY());

		int screenX = actionEvent.getX();
		int screenY = actionEvent.getY();
		int x = actionEvent.getX() + camera.x;
		int y = actionEvent.getY() + camera.y;

		if (actionEvent.getType() == ActTouchEvent.TYPE_REALEASE) {
			if (currentHold != null) {
				currentHold.setRealeased(true);
				currentHold.setHold(false);
				currentHold.setDragged(false);
				if(isDragLayer){
					onLayerDragEnd(currentHold);
				}
			}
			isDragLayer = false;
			isDragCamera = false;
			currentHold = null;
			return true;
		}

		if (isDragLayer) {
			if (actionEvent.getType() == ActTouchEvent.TYPE_HOLD) {
				if(shouldFollowCameraWhenDrag() && currentHold.willfollowCameraWhenDrag()){
					camera.follow(screenX, screenY);
				}
				if (currentHold.canDragFree()) {
					currentHold.x = dragOffX + x;
					currentHold.y = dragOffY + y;
				}
				if (currentHold.canDragHorizontal()) {
					currentHold.x = dragOffX + x;
				}
				if (currentHold.canDragVertical()) {
					currentHold.y = dragOffY + y;
				}
				currentHold.checkBoarderWhenDrag();
				log("actionPerformed(); currentHold.x = " + currentHold.x
						+ "; currentHold.y = " + currentHold.y);
				onLayerDragged(currentHold);
			}
			return true;
		}

		if (isDragCamera) {
			if (actionEvent.getType() == ActTouchEvent.TYPE_HOLD) {
				camera.x = dragOffX - actionEvent.getX();
				camera.y = dragOffY - actionEvent.getY();
				if (camera.checkBoarder()) {
					dragOffX = camera.x + actionEvent.getX();
					dragOffY = camera.y + actionEvent.getY();
				}
				log("actionPerformed(); camera.x = " + camera.x
						+ "; camera.y = " + camera.y);
			}
			return true;
		}

		if (actionEvent.getType() == ActTouchEvent.TYPE_PRESS) {
			if (currentFocus != null
					&& !currentFocus.getTouchRect().contains(x, y)) {
				currentFocus.setFocused(false);
				currentFocus = null;
			}
		}
		
		if(doLayersAction(actionEvent, mLayersHud, mLayersTop, mLayersMiddle, mLayersBottom)){
			return true;
		}

		if (actionEvent.getType() == ActTouchEvent.TYPE_HOLD) {
			if (canDrag()) {
				isDragCamera = true;
				dragOffX = x;
				dragOffY = y;
				log("actionPerformed(); dragOffX = " + dragOffX
						+ "; dragOffX = " + dragOffX);
				return true;
			}
		}
		return false;
	}

	private boolean doLayerAction(Layer layer, ActTouchEvent actionEvent) {
		if (layer.getInnerLayers() != null) {
			for (Layer l : layer.getInnerLayers()) {
				if (doLayerAction(l, actionEvent)) {
					return true;
				}
			}
		}
		int x = actionEvent.getX();
		int y = actionEvent.getY();

		if (layer.getPriority() != Layer.HUD) {
			x += camera.x;
			y += camera.y;
		}

		if (layer.getTouchRect().contains(x, y)) {
			log("layer.getTouchRect() = " + layer.getTouchRect());
			if (actionEvent.getType() == ActTouchEvent.TYPE_PRESS) {
				if (layer.canFoucs() && currentFocus != layer) {
					layer.setFocused(true);
					currentFocus = layer;
				}
			} else if (actionEvent.getType() == ActTouchEvent.TYPE_HOLD) {
				if (currentHold != layer) {
					if (currentHold != null) {
						currentHold.setHold(false);
						currentHold = null;
					}
					layer.setHold(true);
					currentHold = layer;
					holdTimer = 0;
				} else {
					if (layer.canDragFree() || layer.canCopy()
							|| layer.canDragHorizontal()
							|| layer.canDragVertical()) {
						holdTimer++;
						log("hold timer:" + holdTimer);
						if (holdTimer > Config.DRAG_START_TIME) {
							Layer dragLayer = layer;
							if (layer.canCopy()) {
								dragLayer = layer.copy();
								layer.setHold(false);
								layer.setFocused(false);

								dragLayer.setPriority(Layer.MIDDLE);
								addLayer(dragLayer);
								dragLayer.clearOuterLayer();
								dragLayer.setHold(true);
								dragLayer.setFocused(true);
								currentFocus = dragLayer;
								dragLayer.setX(layer.getAbsoluteX() + camera.x);
								dragLayer.setY(layer.getAbsoluteY() + camera.y);
								dragOffX = layer.getAbsoluteX() - x;
								dragOffY = layer.getAbsoluteY() - y;
								currentHold = dragLayer;
							} else {
								dragOffX = dragLayer.x - x;
								dragOffY = dragLayer.y - y;
							}
							isDragLayer = true;
							onLayerDragStart(dragLayer);
							dragLayer.setDragged(true);
						}
					}
				}
			} else if (actionEvent.getType() == ActTouchEvent.TYPE_REALEASE) {
				if (currentHold != null) {
					currentHold.setHold(false);
					currentHold = null;
				}
			}
			return true;
		} else {
			if (currentHold == layer) {
				currentHold.setHold(false);
				currentHold = null;
			}
		}
		return false;
	}
	
	public void onLayerDragStart(Layer dragLayer){
		
	}
	
	public void onLayerDragged(Layer dragLayer){
		
	}
	
	public void onLayerDragEnd(Layer dragLayer){
		
	}

	static void log(String msg) {
		if (Config.LOG_ENABLE) {
			Log.d(Config.LOG_TAG + "-" + TAG, msg);
		}
	}

}
